package enge.checkers;

import java.util.Observable;
import java.util.Observer;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * The MazeView class, which is the graphical representation of the maze being
 * drawn and to be solved. Handles touching of the maze (drawing) and displaying
 * of the changed maze when it is edited, or solved.
 * 
 * @author Kyle Parker (kyle93)
 * @author Blake Whaley
 * @version 2011.11.02
 */
public class CheckerView
    extends View
{

    private CheckerBoard board;

    private boolean      toggleState;

    private Paint        blackPaint       = new Paint();
    private Paint        whitePaint       = new Paint();
    private Paint        redPaint         = new Paint();
    private Paint        bluePaint        = new Paint();
    private Paint        selectedPaint    = new Paint();
    private Paint        kingPaintPlayer1 = new Paint();
    private Paint        kingPaintPlayer2 = new Paint();
    private Checker      prevCheck;

    private Rect[][]     cells;

    private Rect[][]     borders;
    boolean              moving;


    /**
     * Creates a new MazeView. Instantiates the maze, and adds a mazeobserver to
     * it. Sets the toggleState (state of the toggle button for drawing
     * purposes) to false. Sets up all of the different paints as needed, and
     * creates the arrays of rectangles needed for drawing.
     * 
     * @param context
     *            the default parameter for a view.
     * @param attrs
     *            the default parameter for a view.
     */
    public CheckerView(Context context, AttributeSet attrs)
    {
        super(context, attrs);

        blackPaint.setStyle(Paint.Style.FILL);
        blackPaint.setColor(Color.BLACK);

        whitePaint.setStyle(Paint.Style.FILL);
        whitePaint.setColor(Color.WHITE);

        redPaint.setStyle(Paint.Style.FILL);
        redPaint.setColor(Color.RED);

        bluePaint.setStyle(Paint.Style.FILL);
        bluePaint.setColor(Color.BLUE);

        selectedPaint.setStyle(Paint.Style.FILL);
        selectedPaint.setColor(Color.GREEN);

        kingPaintPlayer1.setStyle(Paint.Style.FILL);
        kingPaintPlayer1.setColor(Color.MAGENTA);

        kingPaintPlayer2.setStyle(Paint.Style.FILL);
        kingPaintPlayer2.setColor(Color.CYAN);

        cells = new Rect[8][8];
        moving = false;

    }


    /**
     * Measures the width and height of the view on the starting of the
     * application, and sets them to be the same, making the maze square.
     * 
     * @param widthMeasureSpec
     *            default parameter for the method.
     * @param heightMeasureSpec
     *            default parameter for the method.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // Choose the smallest of the two dimensions to use for both.
        int measureSpec = Math.min(widthMeasureSpec, heightMeasureSpec);

        // Call the superclass implementation but pass it our modified width
        // and height instead of the incoming ones.
        super.onMeasure(measureSpec, measureSpec);
    }


    /**
     * Draws the grid for the maze, as well as the graphical representation of
     * the maze whenever the maze is changed. Does so by using an array of Rects
     * and a set of paints to draw stroke rectangles (the grid) and filled
     * rectangles with a different color depending on the corresponding MazeCell
     * value in the maze.
     * 
     * @param canvas
     *            the canvas to draw on, default for the method.
     */
    @Override
    protected void onDraw(Canvas canvas)
    {
        /*
         * Draws the rectangles that are on the board.
         */
        float myNum = getWidth() / 8;
        for (int x = 0; x < 8; x++)
        {
            for (int y = 0; y < 8; y++)
            {
                cells[x][y] =
                    new Rect((int)myNum * x, (int)myNum * y, (int)myNum
                        * (x + 1), (int)myNum * (y + 1));
            }
        }

        /*
         * Same thing, draws squares for the board.
         */
        for (int x = 0; x < 8; x++)
        {
            for (int y = 0; y < 8; y++)
            {
                if (x % 2 == 0 && y % 2 == 0)
                {
                    canvas.drawRect(cells[x][y], blackPaint);
                }
                else if (x % 2 != 0 && y % 2 == 0)
                {
                    canvas.drawRect(cells[x][y], whitePaint);
                }
                else if (x % 2 != 0 && y % 2 != 0)
                {
                    canvas.drawRect(cells[x][y], blackPaint);
                }
                else
                {
                    canvas.drawRect(cells[x][y], whitePaint);
                }
            }
        }

        /*
         * These two for loops print each "visible" item of the arrays that hold
         * the data for the pieces in the player1 and player2 objects. This way
         * when we try to move a piece we can just update those arrays and it
         * will redraw the screen appropriately.
         */
        float radius = getWidth() / 16;
        for (int x = 0; x < 8; x++)
        {
            for (int y = 0; y < 8; y++)
            {
                if (board.getPiece(x, y) != null
                    && board.getPiece(x, y).getPlayer() == PlayerType.ONE
                    && !board.getPiece(x, y).isSelected())
                {
                    if (board.getPiece(x, y).isKing())
                    {
                        canvas.drawCircle(
                            radius * ((float)(x + .5) * 2),
                            (float)(radius * ((y + .5) * 2)),
                            radius,
                            kingPaintPlayer1);
                        canvas.drawText("K", x, y, redPaint);
                    }
                    else
                    {
                        canvas.drawCircle(
                            radius * ((float)(x + .5) * 2),
                            (float)(radius * ((y + .5) * 2)),
                            radius,
                            redPaint);
                    }
                }
                else if (board.getPiece(x, y) != null
                    && board.getPiece(x, y).isSelected())
                {
                    canvas.drawCircle(
                        radius * ((float)(x + .5) * 2),
                        (float)(radius * ((y + .5) * 2)),
                        radius,
                        selectedPaint);
                }
                else if (board.getPiece(x, y) != null)
                {
                    if (board.getPiece(x, y).isKing())
                    {
                        canvas.drawCircle(
                            radius * ((float)(x + .5) * 2),
                            (float)(radius * ((y + .5) * 2)),
                            radius,
                            kingPaintPlayer2);
                        canvas.drawText("K", x, y, bluePaint);
                    }
                    else
                    {
                        canvas.drawCircle(
                            radius * ((float)(x + .5) * 2),
                            (float)(radius * ((y + .5) * 2)),
                            radius,
                            bluePaint);
                    }
                }
            }
        }
    }


    /**
     * Returns the checker piece at the given coordinates.
     * 
     * @param x
     *            the x coordinate.
     * @param y
     *            the y coordinate.
     * @return a Checker or null if the checker at that point.
     */
    public Checker getChecker(float x, float y)
    {
        int xC = (int)(x / (getHeight() / 8));
        int yC = (int)(y / (getHeight() / 8));
        if (board.getTurn())
        {
            return board.getPiece(xC, yC);
        }
        else
        {
            return board.getPiece(xC, yC);
        }
    }


    public int arrayLocX(float x)
    {
        return (int)(x / (getHeight() / 8));
    }


    public int arrayLocY(float y)
    {
        return (int)(y / (getHeight() / 8));
    }


    /**
     * Changes the view depending on the action.
     * 
     * @param e
     *            the MotionEvent type (DOWN, MOVE, etc) for the touch
     * @return returns true if the action was successful, false if it was not.
     */
    @Override
    public boolean onTouchEvent(MotionEvent e)
    {
        boolean handled = false;
        if (e.getAction() == MotionEvent.ACTION_DOWN)
        {
            Checker check = getChecker(e.getX(), e.getY());
            /*
             * If not in the action of moving.
             */
            if (!moving)
            {
                /*
                 * If the checker is not equal to null AND the checker is
                 * visible AND ( (the checker is player one's checker AND it is
                 * player one's turn) OR (the checker belongs to player two and
                 * it is player two's turn) ).
                 */
                if (check != null
                    && check.isVisible()
                    && ((check.getPlayer() == PlayerType.ONE && board.getTurn()) || (check
                        .getPlayer() == PlayerType.TWO && !board.getTurn())))
                {
                    moving = true;
                    handled = true;
                    prevCheck = check;
                    if (!board.getJump()
                        || (board.getJump() && check.equals(board.check)))
                    {
                        check.setSelected(true);
                    }
                    board.update();
                }
            }
            else
            {
                int arrayX = arrayLocX(e.getX());
                int arrayY = arrayLocY(e.getY());
                /*
                 * If the selected position is the same as the position of the
                 * selected checker.
                 */
                if (arrayX == (int)prevCheck.getX()
                    && arrayY == (int)prevCheck.getY())
                {
                    moving = false;
                    board.getPiece(arrayX, arrayY).setSelected(false);
                    board.update();
                }
                /*
                 * Else if the move is possible to the selected spot.
                 */
                else if (board.checkPossible(prevCheck, arrayX, arrayY))
                {
                    boolean checkLastKing = false;
                    /*
                     * If the selected checker is a king.
                     */
                    if (prevCheck.isKing())
                    {
                        checkLastKing = true;
                    }
                    // Sets the space on the board where the piece was to null.
                    board.setPiece(
                        (int)prevCheck.getX(),
                        (int)prevCheck.getY(),
                        null);
                    // Sets the x and y coordinates of the checker to the
                    // new values of x and y selected by the user
                    prevCheck.setX(arrayX);
                    prevCheck.setY(arrayY);
                    // Sets the space on the board selected to the new checker.
                    board.setPiece(
                        (int)prevCheck.getX(),
                        (int)prevCheck.getY(),
                        prevCheck);
                    /*
                     * If the piece belongs to player one.
                     */
                    if (prevCheck.getPlayer().equals(PlayerType.ONE))
                    {
                        if (arrayY == 0)
                        {
                            prevCheck.setKing(true);
                        }
                    }
                    /*
                     * If the piece belongs to player two.
                     */
                    if (prevCheck.getPlayer().equals(PlayerType.TWO))
                    {
                        if (arrayY == 7)
                        {
                            prevCheck.setKing(true);
                        }
                    }
                    /*
                     * If the piece is a king.
                     */
                    if (checkLastKing == true)
                    {
                        prevCheck.setKing(true);
                    }
                    moving = false;
                    handled = true;
                    prevCheck.setSelected(false);
                    System.out.println(board.twoPlayer);
                    if (!board.getJump() && !board.twoPlayer)
                    {
                        board.computerMove();
                    }
                    else if (!board.getJump())
                    {
                        board.changeTurn();
                    }
                    board.update();
                }
            }

        }
        if (e.getAction() == MotionEvent.ACTION_UP)
        {
            // invalidate();
            handled = true;
        }

        return handled;
    }


    /**
     * Sets the maze to be the one being used in activity. This way, they are
     * using the same maze operations performed in each class can stay
     * consistent with each other.
     * 
     * @param theMaze
     *            the maze to set the maze equal to.
     */
    public void setBoard(CheckerBoard theBoard)
    {
        board = theBoard;
        board.addObserver(new BoardObserver());
    }


    /**
     * Nested observer class for the Maze. Contains the required update function
     * which allows the MazeView to refresh if the maze changes for any reason.
     * 
     * @author Kyle Parker
     * @author Blake Whaley
     * @version 2011.11.02
     */
    private class BoardObserver
        implements Observer
    {

        /**
         * Causes the view to refresh when the maze changes.
         * 
         * @param observable
         *            the observable object, default for the method.
         * @param data
         *            the data for update, default for the method.
         */
        public void update(Observable observable, Object data)
        {
            postInvalidate();
        }
    }
}
